/*
 * Server class for echo project
 * Author: Daniel Henderson, Fahad Muzaffar
 * TCSS 430 Summer 2013 
 * University of Washington
 */
package Server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;

import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;


/**
 * Class that handles the server echo part of the program. Will bind
 * port (81 by default) and wait for connections on that port.
 * 
 * @author Daniel Henderson, Fahad Muzaffar
 * @version 1.0
 */
public class Server {

	/**
	 * IP adress for the multicast group.
	 */
	public static String multiCastIp = "230.0.0.1";
	
	/**
	 * Socket for Multicast group.
	 */
	public static int multiCastPort = 4446;
	
	/**
	 * help the client and server to communicate.
	 */
	public static Socket socket;
	/**
	 * ServerSocket that the server will accept connections
	 * to.
	 */
	private static ServerSocket serverSocket;
	/**
	 * 
	 */
	public static OutputStream outputStream;
	
	/**
	 * Socket that will be created to establish a connection
	 * to a client.
	 */
	private static Socket clientSocket;
	
	/**
	 * Integer value of the open port the server will establish
	 * connections on.
	 */
	private static int portNumber;
	
	/**
	 * Boolean flag as to whether a client is connected 
	 * currently.
	 */
	private static boolean isConnected = false;
	
	/**
	 * Buffer Reader object to receive messages from 
	 * client on the Socket.
	 */
	public static BufferedReader br;
	
	/**
	 * Socket to be used for Multiclass socket
	 */
	private static MulticastSocket mSocket;
	
	/**
	 * Primary method to run the server program.
	 * Individual threads will be created for sending
	 * and receiving data.
	 * 
	 * @param args[1] Argument for parameters (not needed for server)
	 * @throws IOException
	 */
	public static void main(String args[]) throws IOException {
		portNumber = 81;	
		System.out.println("Attempting to bind to port " + portNumber);
		int valid = 0;
		while (valid == 0) {
			try {
				//Create server socket on specified port number
				serverSocket = new ServerSocket(portNumber);
				System.out.println("Binding to port successful.");
				System.out.println("TCP Server Waiting for client connections on port: " + portNumber);
				valid = 1;
			} catch (IOException e) {
				//If Socket cannot be bound, try socket # socket + 1
				System.out.println("Could not bind to port: " + portNumber);
				System.out.println("Changing port number to: " + (portNumber + 1));
				portNumber += 1; // increase to +1 port since that port used
				valid = 0;
			}
		}
		
		try {

			mSocket  = new MulticastSocket(multiCastPort);
			System.out.println("MULTICAST GROUP CREATED - IP:" + multiCastIp + " and Port: " + multiCastPort);
			clientSocket = serverSocket.accept();
			// MESSAGE VERIFYING CONNECTION
			if(!isConnected) {
				System.out.println("Connection established successfully on port: " + portNumber);
				System.out.println("Client " + clientSocket.getRemoteSocketAddress().toString() + " is connected.");
				isConnected = true;
			}
		} catch (IOException e) {
			System.out.println("Connection failed on port: " + portNumber);
		}
		
		
		/**
		 * Inner class that handles the Receiving of Data
		 *
		 */
		class Recieve implements Runnable {
			String importantMessage;
			
			@Override
			public void run() {
				
				while(true) {
					try {
						br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));				
					} catch (IOException e) {
						e.printStackTrace();
					}
					try {
						importantMessage = br.readLine();
						if(importantMessage == null) {
							System.out.println("Connection with client lost....");
							System.out.println("Enter Q or q to quit");
							break;
						}
						else {
							newMClient();
						}
					} catch (IOException e) {
						System.out.println("Disconnected from client.....quitting");
						e.printStackTrace();
						break;
					}
					System.out.println("RECIEVED: " + importantMessage);
				}			
			}
		
			private void newMClient() {
				byte [] theMessage = null;
				InetAddress groupIP = null;
				
				//Create Multicast socket
				try {
					mSocket = new MulticastSocket( );
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				
				//Get Input from Buffer
				theMessage = importantMessage.getBytes();

				//Create Multicast Group
				try {
					groupIP = InetAddress.getByName(multiCastIp);
				} catch (UnknownHostException e) {
						System.out.println("Error: Could not create group IP address Object for multicast channel");
					e.printStackTrace();
				}
				
				//Create Datagram
				DatagramPacket outgoingPacket;
				outgoingPacket = new DatagramPacket(theMessage, theMessage.length, groupIP, multiCastPort);
				
				//Send Datagram
				try {
					mSocket.send(outgoingPacket);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		
		/**
		 * 
		 * Inner class that handles the sending of data.
		 *
		 */
		class Send implements Runnable {

			@Override
			public void run() {
				while(true) {
					System.out.println("SEND MESSAGE (Type Q or q to Quit): ");
					String userInput = null;
					outputStream = null;
					try {
						outputStream = clientSocket.getOutputStream();
					} catch (IOException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
					PrintWriter printWriter = new PrintWriter(outputStream, true);

					BufferedReader userInputBR = new BufferedReader(new InputStreamReader(System.in));
					
					try {
						userInput = userInputBR.readLine();
					} catch (IOException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
					printWriter.println(userInput);

					if ("q".equalsIgnoreCase(userInput) || userInput == null) {
						System.out.println("Q was hit, Exitting");
						try {
							userInputBR.close();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						printWriter.close();
						try {
							clientSocket.close();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						System.exit(0);
					}
				}

				
			}
			
		}

		/*
		 * Create the Threads for the Receiver and Sender class
		 * Start each thread.
		 */
		
		Recieve reciever = new Recieve();
		new Thread(reciever).start();
		
		Send sender = new Send();
		new Thread(sender).start();
	}
}
